package com.wuzheng.wisgo.sanitation.framework.base.handlers;

import com.wuzheng.wisgo.sanitation.framework.core.constant.SysRespEnum;
import com.wuzheng.wisgo.sanitation.framework.core.exception.ServiceException;
import com.wuzheng.wisgo.sanitation.framework.core.protocol.Result;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author：hibon
 * @date：Created in 2019/08/21
 * @description：全局 验证 异常 处理
 * @modified By：
 * @version: 1.0
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
public class GlobalExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @Value("${spring.profiles.active:dev}")
    public String activeProfile;

    private static final String DEV_MODE = "dev";
    private static final String TEST_MODE = "test";


    private String getMessage(Exception exception, SysRespEnum sysRespEnum) {
        if (activeProfile.equalsIgnoreCase(DEV_MODE) || activeProfile.equalsIgnoreCase(TEST_MODE)) {
            return exception.getMessage() != null ? exception.getMessage() : sysRespEnum.getMessage();
        } else {
            return sysRespEnum.getMessage();
        }
    }

    /**
     * 构建返回错误
     *
     * @param sysRespEnum
     * @param exception
     * @param data
     * @return
     */
    private Result failure(SysRespEnum sysRespEnum, Exception exception, Object data) {
        String meesage = null;
        if (exception != null) {
            meesage = this.getMessage(exception, sysRespEnum);
        }
        return Result.buildFailure(sysRespEnum.getCode(), meesage, data);
    }

    /**
     * 构建返回错误
     *
     * @param sysRespEnum
     * @param data
     * @return
     */
    private Result failure(SysRespEnum sysRespEnum, Object data) {
        return failure(sysRespEnum, null, data);
    }

    /**
     * 构建返回错误
     *
     * @param sysRespEnum
     * @param exception
     * @return
     */
    private Result failure(SysRespEnum sysRespEnum, Exception exception) {
        return failure(sysRespEnum, exception, null);
    }

    /**
     * 构建返回错误
     *
     * @param sysRespEnum
     * @return
     */
    private Result failure(SysRespEnum sysRespEnum) {
        return Result.buildFailure(sysRespEnum);
    }

    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ConstraintViolationException.class)
    public Result handleValidationException(ConstraintViolationException e) {
        logger.error(SysRespEnum.ILLEGAL_DATA.getMessage() + ":" + e.getMessage());
        List<Map<String, Object>> fields = new ArrayList<>();
        for (ConstraintViolation<?> cv : e.getConstraintViolations()) {
            String fieldName = ((PathImpl) cv.getPropertyPath()).getLeafNode().asString();
            String message = cv.getMessage();
            Map<String, Object> field = new HashMap<>();
            field.put("field", fieldName);
            field.put("message", message);
            fields.add(field);
        }
        return failure(SysRespEnum.ILLEGAL_DATA, fields);
    }

    @ResponseBody
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleBindException(BindException e) {
        logger.error(SysRespEnum.ILLEGAL_DATA.getMessage() + ":" + e.getMessage());
        List<Map<String, Object>> fields = new ArrayList<>();
        for (FieldError error : e.getFieldErrors()) {
            Map<String, Object> field = new HashMap<>();
            field.put("field", error.getField());
            field.put("message", error.getDefaultMessage());
            fields.add(field);
        }
        return failure(SysRespEnum.ILLEGAL_DATA, fields);
    }

    @ResponseBody
    @ExceptionHandler(MultipartException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleMultipartException() {
        return failure(SysRespEnum.MULTIPART_TOO_LARGE);
    }

    @ResponseBody
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleIllegalArgumentException(IllegalArgumentException e) {
        logger.error(SysRespEnum.ILLEGAL_ARGUMENT.getMessage() + ":" + e.getMessage());
        return failure(SysRespEnum.ILLEGAL_ARGUMENT, e);
    }

    @ResponseBody
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleMissingServletRequestParameterException(
            MissingServletRequestParameterException e) {
        logger.error(SysRespEnum.MISSING_ARGUMENT.getMessage() + ":" + e.getMessage());
        return failure(SysRespEnum.MISSING_ARGUMENT, e);
    }

    @ResponseBody
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleMethodArgumentTypeMismatchExceptionException(
            MethodArgumentTypeMismatchException e) {

        logger.error(SysRespEnum.ILLEGAL_ARGUMENT_TYPE.getMessage() + ":" + e.getMessage());
        return failure(SysRespEnum.ILLEGAL_ARGUMENT_TYPE, e);
    }

    @ResponseBody
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Result handleException(Exception e) {
        e.printStackTrace();
        logger.error(SysRespEnum.INTERNAL_SERVER_ERROR.getMessage() + ":" + e.getMessage());
        return failure(SysRespEnum.INTERNAL_SERVER_ERROR, e);
    }

    @ResponseBody
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {

        logger.error(SysRespEnum.METHOD_NOT_ALLOWED.getMessage() + ":" + e.getMessage());
        return failure(SysRespEnum.METHOD_NOT_ALLOWED, e);
    }

    @ResponseBody
    @ExceptionHandler(ServiceException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleServiceException(ServiceException e) {
        logger.error(SysRespEnum.SERVICE_EXCEPTION.getMessage() + ":" + e.getMessage());
        if (e.getCode() == null) {
            return failure(SysRespEnum.SERVICE_EXCEPTION, e);
        } else {
            return Result.buildFailure(e.getCode(), e.getMessage());
        }
    }

    @ResponseBody
    @ExceptionHandler(IllegalStateException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result handleIllegalStateException(IllegalStateException e) {
        logger.warn("exception", e);
        return failure(SysRespEnum.ILLEGAL_STATE, e);
    }
}
